/**
 * 单词种别码设计：
 *    =   1
 *    ?   2
 *    +   3
 *    -   4
 *    *   5
 *    /   6
 *    (   7
 *    )   8
 *    v   9
 *    c   10
 *    clear 11
 *    #   12
 *    N   13
 */

// typedef struct
// {
//     char IDentifier[30]; // 标识符长度
//     int value;           // 定义标识符代表的数值
// } IDentifierTable;

// typedef struct
// {
//     int syn;   // 种别码
//     int value; // 数值或者标识符入口指针
// } SymbolTbl;

// 此处简单起见，就只考虑标识符，运算符，数字
// 定义一个结构存储（种别码、单词的入口地址）或者（种别码，常数值）或者（种别码）

let sentence = '\0';
let __syn = -1;
let name = '';
let __value;
let __scan_point = 0; // 从开始处扫描
let __id_point; // 定义标识符表入口指针
let __sym_pointer = 0;
let entry;

/**
 * 插入到标识符表中
 * @param {*} name 插入名字
 * @param {*} entry 标识符表索引
 * @param {*} value 插入数值
 */
const InsertID = (name, entry, value) => {
    idTbl[entry].IDentifier = name;
    idTbl[entry].value = value;
};

/**
 * 查找符号表中是否有空闲位置
 * @param {*} name
 */
const ScanEntry = name => {
    const index = idTbl.findIndex(item => item.IDentifier === name);

    return index;
};

/**
 * 插入到符号表中
 * @param {*} syn 种别码
 * @param {*} value 数值或者标识符入口指针
 * @param {*} __pointer
 */
const Insert_Into_SymbolTbl = (syn, value) => {
    symbol[__sym_pointer].syn = syn;
    symbol[__sym_pointer].value = value;
    __sym_pointer++;
};

/**
 * 查找表中是否已经存在该标识符
 * @param {*} id
 */
const IsExist = id => {
    const index = idTbl.findIndex(item => item.IDentifier === id);

    return index > -1;
};

/**
 * 查找表中空闲入口
 */
const Search_Free_Entity = () => {
    const index = idTbl.findIndex(item => !item.IDentifier);

    return index;
};

/**
 * 判断是否是字母
 * @param {*} ch
 */
const IsLetter = ch => {
    const chCode = ch.charCodeAt();
    return ch === '_' || (chCode >= 97 && chCode <= 122) || (chCode >= 65 && chCode <= 90);
};

/**
 * 判断是否为数字
 * @param {*} digit
 */
const IsDigit = digit => {
    return /^\d$/.test(digit);
};

/**
 * 扫描器
 * @param {*} sentence
 * @param {*} name
 * @param {*} __syn
 * @param {*} __scan_point
 * @param {*} __value
 */
const Scanner = sentence => {
    let token = [];
    let ch;
    let p_token = 0; // token 的指针
    let digit_value = 0; // 记录常量的大小
    for (let n = 0; n < 30; n++) {
        token[n] = '\0'; // 字符数组清零
    }

    ch = sentence[__scan_point]; // 读入一个字符
    // 该字符的种别码已经在主程序中登记了
    if (ch === '#' && __scan_point === 0) {
        __scan_point++; // 刚开始的第一个字符一定为 '#'
        ch = sentence[__scan_point]; // 指针下移，扫描下一字符
    }

    if (IsLetter(ch)) {
        while (IsLetter(ch) || IsDigit(ch)) {
            token[p_token++] = ch;
            __scan_point++;
            ch = sentence[__scan_point]; // 读入一个字符
        }
        token[p_token] = '\0';

        __syn = 9; // 代表找到了标识符

        const tokenStr = token.join('').replace(/\0/g, '');

        // 代表找到了保留字“clear”
        if (tokenStr === 'clear') {
            __syn = 11;
        }

        name = tokenStr;
    } else if (IsDigit(ch)) {
        digit_value = 0;
        while (IsDigit(ch)) {
            digit_value = digit_value * 10 + ch * 1 - '0';
            __scan_point++;
            ch = sentence[__scan_point]; // 读入一个字符
        }
        __value = digit_value; // 带回整数值
        __syn = 10;
    } else {
        switch (ch) {
            case '=':
                __syn = 1;
                break;
            case '?':
                __syn = 2;
                break;
            case '+':
                __syn = 3;
                break;
            case '-':
                __syn = 4;
                break;
            case '*':
                __syn = 5;
                break;
            case '/':
                __syn = 6;
                break;
            case '(':
                __syn = 7;
                break;
            case ')':
                __syn = 8;
                break;
            case '#':
                __syn = 12;
                break;
            default:
                console.log('输入句子有误！\n');
                break;
        }
        __scan_point++;              // 保持指针始终指向待判断的字符
        ch = sentence[__scan_point]; // 读入一个字符
    }
};

/**
 * 检查句子是不是#。。。#的形式
 * @param {*} sentence
 */
const Check_Is_Right = sentence => {
    return sentence[0] === '#' && sentence[sentence.length - 1] === '#';
};

/**
 * 将符号表的syn全部设置为0
 */
const Clear_Symbol_Tbl = () => {
    for (let i = 0; i < 100; i++) {
        symbol[i].syn = 0; // 代表没定义
        symbol[i].value = -1; // 指定为-1
    }
};

/**
 * 清空标识符表
 */
const Clear_IDentifier_Tbl = () => {
    for (let i = 0; i < 30; i++) {
        idTbl[i].IDentifier = '';
        idTbl[i].value = -1;
    }
};

/**
 * 初始化变量
 */
const initGlobalArgs = () => {
    sentence = '\0';
    __syn = -1;
    name = '';
    __value;
    __scan_point = 0; // 从开始处扫描
    __id_point; // 定义标识符表入口指针
    __sym_pointer = 0;
    entry = 0;
}

/**
 * 词法分析
 */
const sentence_input_ok = () => {
    initGlobalArgs();

    const sentenceInput = document.getElementById('sentence');
    sentence = sentenceInput.value;

    console.log('----Check_Is_Right(sentence): ', Check_Is_Right(sentence));
    if (Check_Is_Right(sentence)) {
        // 将 # 插入 symbol表
        Insert_Into_SymbolTbl(12, -1, __sym_pointer);

        // 开始对输入 sentence 进行词法分析，一直扫描到第二个 '#'
        while (__syn !== 12) {
            Scanner(sentence, name, __syn, __scan_point, __value);
            // 若是单词
            switch (__syn) {
                case 9:
                    // printf('(%d , %s)\n', __syn, name);
                    // 登记到表中
                    if (!IsExist(name)) {
                        // 不存在则插入
                        // 查找可插入位置
                        __id_point = Search_Free_Entity();
                        InsertID(name, __id_point, -1); // -1代表还没被赋值
                    }
                    // 搜索该标识符的入口地址放入value中
                    entry = ScanEntry(name);
                    Insert_Into_SymbolTbl(__syn, entry, __sym_pointer);
                    break;
                case 10: //常数
                    Insert_Into_SymbolTbl(__syn, __value, __sym_pointer);
                    // console.log('__syn, __value: ', __syn, __value);
                    // printf('(%d  , %d)\n', __syn, __value);
                    break;
                default:
                    console.log('__syn: ', __syn);
                    // printf('(%d ,  )\n', __syn);
                    Insert_Into_SymbolTbl(__syn, -1, __sym_pointer); // -1代表该处不需要值
            }
        }

        let idTblDomStr = `<div>idTbl:\n`
        const idTblDomStrH = `<div class="idTbl_cell">
            <div>标识符的入口地址</div>
            <div>标识符</div>
            <div>标识符的值(-1代表没被赋值)</div>
        </div>`
        idTblDomStr += idTblDomStrH;
        // 标识符表
        for (let m1 = 0; m1 < 30; m1++) {
            if (idTbl[m1].IDentifier !== '') {
                const idTblDomStrB = `<div class="idTbl_cell">
                    <div>${m1}</div>
                    <div>${idTbl[m1].IDentifier}</div>
                    <div>${idTbl[m1].value}</div>
                </div>`;

                idTblDomStr += idTblDomStrB;
            }
        }
        idTblDomStr += `</div>`;
        const idTblEle = document.getElementById('idTbl');
        idTblEle.innerHTML = idTblDomStr;

        let symbolDomStr = `<div>symbol:\n`
        const symbolDomH = `<div class="idTbl_cell">
            <div>符号表的入口地址</div>
            <div>种别码</div>
            <div>具体的值(或标识符入口)</div>
        </div>`
        symbolDomStr += symbolDomH;
        // 符号表
        for (let m2 = 0; m2 < __sym_pointer; m2++) {
            const symbolDomB = `<div class="idTbl_cell">
                <div>${m2}</div>
                <div>${symbol[m2].syn}</div>
                <div>${symbol[m2].value}</div>
            </div>`;

            symbolDomStr += symbolDomB;
        }
        symbolDomStr += `</div>`;
        const symbolDom = document.getElementById('symbol');
        symbolDom.innerHTML = symbolDomStr;

        MainProc_Analysis();
    }
};
